
package astrolabe.imgprocessing;

import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.util.LinkedList;
import javax.swing.*;
import java.awt.*;
import java.net.*;

import javax.media.jai.*;
import com.sun.media.jai.codec.*;

public class Visor {
    
    
    
    public static PlanarImage findEdges(PlanarImage src)
    {
         float data_h[] = new float[] { 1.0F,   0.0F,   -1.0F,
                                        1.414F, 0.0F,   -1.414F,
                                        1.0F,   0.0F,   -1.0F};
         float data_v[] = new float[] {-1.0F,  -1.414F, -1.0F,
                                        0.0F,   0.0F,    0.0F,
                                        1.0F,   1.414F,  1.0F};

        KernelJAI kern_h = new KernelJAI(3,3,data_h);
        KernelJAI kern_v = new KernelJAI(3,3,data_v);
     
        return (PlanarImage)JAI.create("gradientmagnitude", src,kern_h, kern_v);
    }
    
    public static PlanarImage scale(PlanarImage img, float scale)
    {
        ParameterBlock pb = new ParameterBlock();

        pb.addSource(img);
        pb.add(scale);
        pb.add(scale);
        
        return JAI.create("scale", pb);
    }
        
    public static PlanarImage binarize(PlanarImage img)
    {
        //                       RED GREEN  BLUE
        double[][] clevels = {{ 1.0D, 1.0D, 0.3D, 0D }};
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(img);
        pb.add(clevels);
        PlanarImage src = JAI.create("BandCombine", pb, null);

        Histogram histogram = (Histogram)JAI.create("histogram", src).getProperty("histogram");
        double[] threshold = histogram.getPTileThreshold(0.99);

        return JAI.create("binarize", src, new Double(threshold[0]));
    }    
    
    public static double mean(PlanarImage img, int x, int y)
    {
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(img);
        pb.add(null);
        pb.add(x);
        pb.add(y);
        PlanarImage imgmean = JAI.create("mean", pb);
        
        return ((double[])imgmean.getProperty("mean"))[0];        
    }
    
    public static PlanarImage cropTile(PlanarImage img, float tileSize, float x, float y)
    {
        ParameterBlock pb = new ParameterBlock();

        pb.addSource(img);
        pb.add(x);
        pb.add(y);
        pb.add(tileSize);
        pb.add(tileSize);

        return JAI.create("crop", pb);
    }    

    public static PlanarImage getBand(PlanarImage img, int band)
    {
         // Set the indices of three bands of the image.
         int[] bandIndices = { band };

         // Construct the ParameterBlock.
         ParameterBlock pb = new ParameterBlock();
         pb.addSource(img);
         pb.add(bandIndices);

         // Perform the operation
         return JAI.create("bandSelect",pb);

    }
    
    public static PlanarImage sub(PlanarImage keep, PlanarImage remove)
    {
         return JAI.create("subtract",keep,remove);
    }
    
    public static PlanarImage and(PlanarImage desired, PlanarImage disposable)
    {
         ParameterBlock pb = new ParameterBlock();
         //pb.addSource(disposable);
         
         //PlanarImage notDisposable=JAI.create("not",pb);
         
         pb=new ParameterBlock();
         pb.addSource(desired);
         pb.addSource(disposable);
         
         // Perform the operation
         return JAI.create("and",pb);

    }
    
    public static boolean[][] makeMap(BufferedImage img)
    {
        int w = img.getWidth();
        int h = img.getHeight();
        boolean map[][] = new boolean[w][h];
        
        for(int x=0;x<w;x++)
            for(int y=0;y<h;y++)
                map[x][y]=(img.getData().getSample(x,y,0)!=0);;
                
        return map;
    }
        
    public static void printMap(boolean[][] map, Point botPos, Point gPos, Point rPos, LinkedList <Point>path)
    {
        int w = map.length;
        int h = map[0].length; 
        boolean pfound = false;
        
        for(int y=0;y<h;y++)
        {
            for(int x=0;x<w;x++)
            {
                pfound=false;
        
                for(Point p: path)
                {
                    if(p.equals(new Point(x,y)))
                    {
                        System.out.print("O");
                        pfound=true;
                    }
                }
                if(pfound==false)
            {
                if(botPos.equals(new Point(x,y)))
                    System.out.print("X");
                else if(gPos.equals(new Point(x,y)))
                    System.out.print("B");
                else if(rPos.equals(new Point(x,y)))
                    System.out.print("F");
                else if(map[x][y])
                    System.out.print("#");
                else
                    System.out.print(".");
            }
            }
            System.out.println();
        }
        
    }

        
    public static PlanarImage AwtToPlanar(Image awtImg)
    {
        return JAI.create("AWTImage", awtImg);
    }
    
    public static Line2D findBot(PlanarImage img, float scale, float mapScale)
    {
        img =Visor.scale(img, scale);
        ParameterBlock pb = new ParameterBlock();
        
        PlanarImage bband=Visor.getBand(img,2);
        PlanarImage gband=Visor.getBand(img,1);
        PlanarImage rband=Visor.getBand(img,0);
        
        pb.addSource(JAI.create("subtract",gband,JAI.create("max",bband,rband)));//Visor.sub(gband,Visor.sub(JAI.create("subtractconst",bband,new double[] {128d}),rband)));
        pb.add(null);
        pb.add(1);
        pb.add(1);
        pb.add(true);
        pb.add(1);
        java.util.List greenList = ((java.util.List[])JAI.create("extrema",pb).getProperty("maxlocations"))[0];
        
        pb = new ParameterBlock();
        pb.addSource(JAI.create("subtract",rband,JAI.create("max",bband,gband)));
        pb.add(null);
        pb.add(1);
        pb.add(1);
        pb.add(true);
        pb.add(1);
        java.util.List redList = ((java.util.List[])JAI.create("extrema",pb).getProperty("maxlocations"))[0];
        
        int[] ag = (int[])greenList.get(0);
        int[] ar = (int[])redList.get(0);
        Point gPos = new Point((int)(ag[0]*mapScale/scale),(int)(ag[1]*mapScale/scale));
        Point rPos = new Point((int)(ar[0]*mapScale/scale),(int)(ar[1]*mapScale/scale));
        
        return (new Line2D.Double(gPos,rPos)); 
    }
    
    public static double getInitialRot(Line2D botLine)
    {
        double dx = botLine.getP2().getX() - botLine.getP1().getX();
        double dy = botLine.getP2().getY() - botLine.getP1().getY();
        double angle = 0.0d;
 
        if (dx == 0.0) {
            if(dy == 0.0)     angle = 0.0;
            else if(dy > 0.0) angle = Math.PI / 2.0;
            else              angle = (Math.PI * 3.0) / 2.0;
        }
        else if(dy == 0.0) {
            if(dx > 0.0)      angle = 0.0;
            else              angle = Math.PI;
        }
        else {
            if(dx < 0.0)      angle = Math.atan(dy/dx) + Math.PI;
            else if(dy < 0.0) angle = Math.atan(dy/dx) + (2*Math.PI);
            else              angle = Math.atan(dy/dx);
        }
        
        angle+=(Math.PI/2);
        
        if(angle>=2*Math.PI)
            angle-=2*Math.PI;
        if(angle>Math.PI)
            angle-=2*Math.PI;
        
        return Math.toDegrees(angle);
     }
    
    public static Point getBotPos(Line2D botLine)
    {   return (new Point((int)(botLine.getX1()+((botLine.getX2()-botLine.getX1())/2)),(int)(botLine.getY2()+((botLine.getY1()-botLine.getY2())/2))));  }
    
    public static PlanarImage removeBot(PlanarImage img, Line2D botLine, double angle, int pad)
    {
        int x1 = (int)botLine.getX1();
        int x2 = (int)botLine.getX2();
        int y1 = (int)botLine.getY1();
        int y2 = (int)botLine.getY2();
        BufferedImage mask = img.getAsBufferedImage();
        Graphics gMask = mask.getGraphics();
        //gMask.setColor(Color.WHITE);
        //gMask.fillRect(0,0,img.getWidth(),img.getHeight());
        gMask.setColor(Color.BLACK);
        int[] xs=new int[5];
        int[] ys=new int[5];
        if(angle>-23.5 && angle<=23.5)          // |
        {
            int txs[]={x2-pad,x2+pad,x1+pad,x1-pad,x2-pad};
            int tys[]={y2-pad,y2-pad,y1+pad,y1+pad,y2-pad};
            xs=txs;
            ys=tys;
        }
        else if(angle>23.5 && angle<=68.5)      // /
        {
            int txs[]={x2,x2+pad,x1,x1-pad,x2};
            int tys[]={y2-pad,y2,y1+pad,y1,y2-pad};
            xs=txs;
            ys=tys;
        }
        else if(angle>68.5 && angle<=113.5)     // -
        {
            int txs[]={x2+pad,x2+pad,x1-pad,x1-pad,x2+pad};
            int tys[]={y2-pad,y2+pad,y1+pad,y1-pad,y2-pad};
            xs=txs;
            ys=tys;
        }
        else if(angle>113.5 && angle<=158.5)     // \
        {
            int txs[]={x2+pad,x2,x1-pad,x1,x2+pad};
            int tys[]={y2,y2+pad,y1,y1-pad,y2};
            xs=txs;
            ys=tys;
        }
        else if((angle>158.5 && angle<=180) || (angle<=-158.5 && angle>=-180))     // |
        {
            int txs[]={x2+pad,x2-pad,x1-pad,x1+pad,x2+pad};
            int tys[]={y2+pad,y2+pad,y1-pad,y1-pad,y2+pad};
            xs=txs;
            ys=tys;
        }
        else if(angle<-113.5 && angle>=-158.5)     // /
        {
            int txs[]={x2,x2-pad,x1,x1+pad,x2};
            int tys[]={y2+pad,y2,y1-pad,y1,y2+pad};
            xs=txs;
            ys=tys;
        }
        else if(angle<-68.5 && angle>=-113.5)     // -
        {
            int txs[]={x2-pad,x2-pad,x1+pad,x1+pad,x2-pad};
            int tys[]={y2+pad,y2-pad,y1-pad,y1+pad,y2+pad};
            xs=txs;
            ys=tys;
        }
        else if(angle<=-23.5 && angle>=-68.5)     // \
        {
            int txs[]={x2-pad,x2,x1+pad,x1,x2-pad};
            int tys[]={y2,y2-pad,y1,y1+pad,y2};
            xs=txs;
            ys=tys;
        }
        
        gMask.fillPolygon(xs,ys,5);
        
        PlanarImage masked = PlanarImage.wrapRenderedImage(mask);
        
        return masked;
    }
    
    public static BufferedImage process(PlanarImage img, float scale, int padding, Line2D botLine, double angle)
    {
        long start = System.currentTimeMillis();
        
        img=Visor.scale(img,scale);
        
        img = Visor.findEdges(img);
        img=Visor.binarize(img);
        img = removeBot(img, botLine, angle,16);
        
        for(int i=0;i<padding;i++)
            img = Visor.findEdges(img);
        
        return img.getAsBufferedImage();
    }
}
